﻿#pragma once
#include "stdafx.h"
	
#define OgreDialog 1	

namespace OgreFrame
{	
	using namespace Ogre;
	class _InputClass
	{
	private:
		friend class OgreApp;
		HWND      hwnd;
		int       keyup;
		int       keydown;
		DWORD	  MzDelta;
		POINT	  click;
		POINT	  mousepoint;
		POINT     realpoint;
		POINT	  pointofset;
		_InputClass::_InputClass()
		{
			ZeroMemory(this,sizeof(_InputClass));
			this->click.x=-999;
			this->click.y=-999;
		}
		_InputClass::~_InputClass()
		{

		}
		void _InputClass::Init(HWND h)
		{
			this->hwnd=h;
		}
	public:
		void _InputClass::getKeyMessage(MSG m)
		{
			realpoint=m.pt;
			switch(m.message)
			{
			case WM_KEYUP:	
			case WM_MBUTTONUP:
				keyup=m.wParam;
				break;
			case WM_LBUTTONUP:
				keyup=0x01;
				pointofset=mousepoint;
				break;
			case WM_RBUTTONUP:
				keyup=0x02;
				pointofset=mousepoint;
				break;
			case WM_LBUTTONDOWN:
				pointofset=mousepoint;
				click=m.pt;					
				::ScreenToClient(this->hwnd,&click);
				break;
			case WM_RBUTTONDOWN:
				click=m.pt;					
				::ScreenToClient(this->hwnd,&click);
				pointofset=mousepoint;
				break;
			case WM_MOUSEMOVE:
				mousepoint=m.pt;
				::ScreenToClient(this->hwnd,&mousepoint);
				break;
			case WM_MOUSEWHEEL:			  
				MzDelta = m.wParam;
				break;
			};

		}

		POINT	_InputClass::getClickPt()
		{
			return this->click;
		}
		POINT	_InputClass::getNowPt()
		{
			return this->mousepoint;
		}
		POINT	_InputClass::getRealPt()
		{
			return this->realpoint;
		}
		bool	_InputClass::InPoint(const POINT &point)
		{
			if(this->mousepoint.x==point.x && this->mousepoint.y==point.y)
			{
				return true;
			}
			return false;
		}
		bool	_InputClass::InPointClick(const POINT &point)
		{
			if(this->click.x==point.x && this->click.y==point.y)
			{
				return true;
			}
			return false;
		}
		bool	_InputClass::InRect(const RECT &rt)
		{
			if(::PtInRect(&rt,this->mousepoint))
			{
				return true;
			}
			return false;
		}
		bool	_InputClass::InRectClick(const RECT &rt)
		{
			if(::PtInRect(&rt,this->click))
			{
				this->click.x=-1;
				this->click.y=-1;
				return true;
			}
			return false;
		}
		bool	_InputClass::Up(int index)
		{
			if(this->keyup==index)
			{
				this->keyup=0;
				return true;
			}
			return false;
		}
		bool	_InputClass::Down(int index)
		{
			short m=0;
			if(GetActiveWindow()!=this->hwnd)
			{
				return false;
			}
			m=GetAsyncKeyState(index);
			if(m&0x8000)
			{
				return true;
			}
			return false;
		}
		bool	_InputClass::MWhellAhead()
		{
			if( this->MzDelta == 0x00780000)
			{
				this->MzDelta=0;
				return true;	
			}
			else
			{		
				return false;	
			}
		}
		bool	_InputClass::MWhellBack()
		{
			if(this->MzDelta == 0xff880000)
			{
				this->MzDelta=0;
				return true;			
			}
			else
			{	
				return false;
			}

		}
		POINT	_InputClass::GetOfset()
		{
			POINT m=getNowPt();

			m.x=pointofset.x-m.x;
			m.y=pointofset.y-m.y;
			pointofset=getNowPt();
			return m;
		}
	};	
	class _WindowClass 
	{
		friend class OgreApp;
	private:
		WNDCLASSEX wcex;	
		HWND       hwnd;
		HDC        hdc;
		int        Wide,High;	
	private:	
		static	LRESULT CALLBACK windowproc(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam)
		{
			switch(Msg)
			{
			case WM_CLOSE:
				PostQuitMessage(0);
				break;
			}
			return DefWindowProc(hWnd,Msg,wParam,lParam); 

		}
		void	peekMsg(MSG &msg)
		{	
			windowproc(this->hwnd,msg.message,msg.wParam,msg.lParam);
		}
		int		Init(int wide,int high,HINSTANCE hInstance=0)
		{			
			HWND deskwindow=NULL;
			RECT deskrt={NULL};
			deskwindow=GetDesktopWindow();
			GetWindowRect(deskwindow,&deskrt);
			int x=0,y=0;
			x=deskrt.right-wide;
			y=deskrt.bottom-high;

			x=x/2;
			y=y/2;
			TCHAR myclass[100]={TEXT("renderwindow")};
			wcex.cbSize = sizeof(WNDCLASSEX);
			wcex.style			= CS_HREDRAW | CS_VREDRAW;
			wcex.lpfnWndProc	= windowproc;
			wcex.cbClsExtra		= 0;
			wcex.cbWndExtra		= 0;
			wcex.hInstance		= hInstance;
#if IDI_SMALL
			wcex.hIcon			= LoadIcon(hInstance,MAKEINTRESOURCE(IDI_SMALL));
#else
			wcex.hIcon			=NULL;
#endif		
			wcex.hCursor=	LoadCursor(NULL, IDC_ARROW);
			wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+2);
			wcex.lpszMenuName	= NULL;
			wcex.lpszClassName	= myclass;
			wcex.hIconSm		= NULL;
			RegisterClassEx(&wcex);
			deskrt.left=x;
			deskrt.top=y;
			deskrt.right=deskrt.left+wide;
			deskrt.bottom=deskrt.top+high;
			AdjustWindowRectEx(&deskrt,WS_SYSMENU|WS_MINIMIZEBOX,false,0); 
			//set the window width and height with the rect above
			hwnd=CreateWindow(myclass,TEXT("ELEMENT"),WS_SYSMENU|WS_MINIMIZEBOX,x,y,deskrt.right-deskrt.left,deskrt.bottom-deskrt.top,0,0,hInstance,0);
			if(!this->hwnd)
			{
				return 0;
			}
			this->hdc=::GetDC(hwnd);
			this->Wide=wide;
			this->High=high;
			return 1;
		}
		_WindowClass()
		{	
			ZeroMemory(this,sizeof(_WindowClass));
		}
		~_WindowClass(){}
	public:	
		int  getW(){return Wide;}
		int  getH(){return High;}
		HDC  getHDC(){return hdc;}
		HWND getHWND(){return hwnd;}
		void setWindowShow(int nCmdshow)
		{
			::ShowWindow(this->hwnd,nCmdshow);
		}
		void setTimer(int IDevent,int tims,TIMERPROC p=0)
		{						
			::KillTimer(this->hwnd,IDevent);	
			::SetTimer(this->hwnd,IDevent,tims,p);
		}
	};
	class _OgreClass
	{
		friend class OgreApp;
	private:
		IDirect3DDevice9	*mDevice9;
		Ogre::Root			*mRoot;
		Ogre::RenderWindow	*mRenderWindow;
		Ogre::SceneManager	*mSceneManager;
		Ogre::Camera		*mCamera;
	private:
		bool		setupRenderWindow(HWND hwnd,int wide,int high,bool fullScreen)
		{					
			mRoot->initialise(false);
			NameValuePairList misc;
			misc["externalWindowHandle"] = StringConverter::toString((int)hwnd);
			mRenderWindow = mRoot->createRenderWindow("Main RenderWindow", wide, high, fullScreen, &misc);
		
			return true;
		}
		bool		setupCamera()
		{
			mCamera = mSceneManager->createCamera("PlayerCam");
			// Position it at 80 in Z direction
			mCamera->setPosition(Ogre::Vector3(0,0,100));
			// Look back along -Z
			mCamera->lookAt(Ogre::Vector3(0,0,0));
			mCamera->setNearClipDistance(5);
			mRenderWindow->addViewport(mCamera);
			return true;
		}
		bool		setupResouce()
		{		
			ConfigFile cf;
			String secName, typeName, archName;		
#if OGRE_DEBUG_MODE
			cf.load("resources_d.cfg");
#else
			cf.load("resources.cfg");
#endif
			ConfigFile::SectionIterator  seci = cf.getSectionIterator();

			while (seci.hasMoreElements())
			{
				secName = seci.peekNextKey();
				ConfigFile::SettingsMultiMap *settings = seci.getNext();
				ConfigFile::SettingsMultiMap::iterator i;
				for (i = settings->begin(); i != settings->end(); ++i)
				{
					typeName = i->first;
					archName = i->second;
					ResourceGroupManager::getSingleton().addResourceLocation(archName, typeName, secName);
				}
			}
			return	true;
		}		
		bool		setupRenderSysterm()
		{
#if OgreDialog
			if(!mRoot->showConfigDialog())
			{       
				return false;
			}
#else

#if OGRE_DEBUG_MODE
			mRoot->loadPlugin("RenderSystem_Direct3D9_d.dll");
#else
			mRoot->loadPlugin("RenderSystem_Direct3D9.dll");
//#endif
//			RenderSystemList d=mRoot->getAvailableRenderers();
//			mRoot->setRenderSystem(d[0]);
#endif

#endif
			return true;
		}		
		bool		Init(HWND renderwindow,int wide,int high,bool fullScreen)
		{
		
#if OGRE_DEBUG_MODE
			mRoot = OGRE_NEW Root("plugins_d.cfg");
#else
			mRoot = OGRE_NEW Root("plugins.cfg");
#endif
			setupResouce();
			if(!setupRenderSysterm())
			{
				return false;
			}	
			setupRenderWindow(renderwindow,wide,high,fullScreen);

			mSceneManager=mRoot->createSceneManager(ST_GENERIC,"BaseScene");		
			TextureManager::getSingleton().setDefaultNumMipmaps(5);				
			ResourceGroupManager::getSingleton().initialiseAllResourceGroups();

		
			////取D3D指针
			mRenderWindow->getCustomAttribute("D3DDEVICE", &mDevice9);			 
			assert( mDevice9 );	
			setupCamera();
			return true;
		}
		bool		RenderOneFrame()
		{
			return mRoot->renderOneFrame();
		}
		void		Destroy()
		{
			mRoot->destroyAllRenderQueueInvocationSequences();
		}	
	public:
		Camera			*GetCamera()
		{
			return mCamera;
		}
		SceneManager	*GetSceneManager()
		{
			return	mSceneManager;
		}
		Root			*GetRoot()
		{
			return	mRoot;
		}

	};
	
	class OgreApp
	{
	public:
		OgreFrame ::_WindowClass eWindow;
		OgreFrame ::_InputClass  eInput;
		OgreFrame ::_OgreClass   eOgre;
		void Init(int wide ,int high,bool full=false)
		{
			EngineState=0;
			eWindow.Init(800,600);
			eInput.Init(eWindow.getHWND());	
			eOgre.Init(eWindow.getHWND(),eWindow.getW(),eWindow.getH(),full);
			exInit();
		}
	
		void Stop()
		{
			EngineState=0;
			eOgre.Destroy();
		}
		void Run()
		{

			if(EngineState)
			{
				return ;
			}
			eWindow.setWindowShow(SW_SHOW);
			MSG msg;		
			PeekMessage( &msg, NULL, 0U, 0U, PM_NOREMOVE );	
			EngineState=1;
			while( EngineState!=0 )
			{
				if(PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) != 0 )
				{				
					if(msg.message==WM_QUIT)
					{
						if(exExit())
						{
							Stop();
							return ;
						}
					}
					eInput.getKeyMessage(msg);
					eWindow.peekMsg(msg);
				}
				else
				{
					exData();
					eOgre.RenderOneFrame();
					Sleep(33);
				}
			}
		}
	private:
		int						EngineState;
	protected:
		virtual void exData(){}
		virtual void exInit(){}
		virtual bool exExit(){return true;}


	};

}
